home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
BCI NET
/
BCI NET Dec 94.iso
/
archives
/
programming
/
utilities
/
heartbeat.lha
/
HeartBeat
/
hb.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-05-29
|
76KB
|
2,363 lines
/*****************************************************************************
* HeartBeat 1.0 (C) L. Vanhelsuwé 1992-94
* ------------- -------------------------
*
* Written by Laurence Vanhelsuwé in April 1992
*
* HeartBeat is a generic Amiga system call monitor.
* It allows you to monitor a Task's system calls usage or monitor global
* system call activity.
* This program uses the standard Commodore .FD files for all its information
* on available libraries and system calls. This results in a program which
* isn't locked to a certain revision of the Operating System but one that
* evolves with it.
* Additionally, executable size is kept small by not having system call info
* embedded inside the program.
*
* History
* -------
* 06-APR-92: started coding (goal: loading and parsing of all .fd files)
* 07-APR-92: had a look at gadtools stuff (done most of register requester)
* 08-APR-92: (goal: function selection/constant refresh)
* 10-APR-92: modified event loop to more robust global event port system
* 16-APR-92: added new/load/save project options
* 22-APR-92: discovered ASL FileRequest doesn't return full filename! Arghh
* 26-APR-92: added non-ROM vector highlighting (handy for VIRUS detection !)
* 13-JUN-92: added function address printing
* 22-APR-94: Finally cleaned up ASL Filerequester code!
*
* To Dos:
* -------
* - **!! Ensure monitored libraries don't ever get flushed while we run.
* - Fix currentdir change side-effect
* -
* -
*****************************************************************************/
// **!! REQUIRES 2.0 OS !! (CHECKED AT RUN-TIME)
// Include Amiga specific header files
#include <exec/execbase.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <exec/nodes.h>
#include <exec/types.h>
#include <devices/timer.h>
#include <dos/dosextens.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/gadgetclass.h>
#include <libraries/asl.h>
#include <libraries/gadtools.h>
#include <utility/tagitem.h>
// Include standard C headers and Lattice specific headers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// Include function prototype definitions
// Headers coming out of the PROTO directory include #pragmas to use
// registerized system call calling.
#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/utility_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/asl_protos.h>
void NewList( struct List *);
// Include Application private headers
#include "cload_file.h"
// All system functions which are currently being tracked have a context
// structure associated with them so that patch stubs can use this to store
// function-specific (local, contextual) information.
struct Context {
ULONG regvals[16]; // D0..D7, A0..A7
ULONG regmasks[16]; // don't care bits for above
USHORT freeze; // freeze on match or just count ?
USHORT frozen; // wedge froze Task TRUE/FALSE
ULONG called_cnt; // each time func gets called this increments
ULONG trapped_cnt; // each time input registers match template
ULONG last_called_cnt; // last value of above (to optimize update)
ULONG last_trapped_cnt; // last value of above (to optimize update)
APTR stdvector; // ptr to normal ROM/RAM routine for this syscall
};
// Every library function found in the .fd files gets a structure as follows:
struct SysCall {
struct Node func_node; // a node so we can link ya all together
char *funcname; // ptr to name of system call (eg "CurrentDir")
USHORT namelen; // length in bytes of name
SHORT func_LVO; // the _LVO for this function (eg -$C0)
struct Library *parent_lib;// this func belongs to library X
struct Context *ctxt; // ptr to context struct for patch.
};
//-------------------------------------------------------------------------
#define MAX_LIBS 40 // can't cope with more libraries !
#define MAX_FUNCS 1500 // can't cope with more functions (total)
#define MAX_PATCHED 72 // can't cope with more PATCHED functions
#define MAX_FILENAME 512 // maximum absolute filename (LONG PATHS on networks!)
#define POOL_SIZE (MAX_FUNCS*sizeof(struct SysCall))
#define LF 10
#define REFRESH_FREQ 4 // times a second update call counters
// Enumeration type for app_state (application state)
#define FUNCWINDOW 1 // window with function list active
#define MONWINDOW 2 // window with tracked functions active
#define REGSWINDOW 3 // register set requester window active
#define TASKWINDOW 4 // task requester window active
#define WINWIDTH 640 // Need interlace Workbench to open !
#define WINHEIGHT 340
#define PANE_X 5 // offset to Gimme00 area.
#define PANE_Y 12
#define MAX_LABLEN 20 // extra long function names are chopped
#define MAX_TASKLAB 20 // maximum length of a Task name string in req.
#define CHAR_WIDTH 8 // pixel dimensions for Topaz 80
#define CHAR_HEIGHT 8
// Enumeration type for our scan_state variable
#define NEED_NAME 0 // initial state to grab head like * "dos.library"
#define NEUTRAL 1 // neutral state to find out what we're looking at.
#define GET_FUNC 2 // get all other lines
#define GET_COMMAND 3 // get line with ##cmd
#define ERR_OK 0 // no error occurred
#define ERR_BADFILE 1 // incorrect file format
#define ERR_DOSERR 2 // failed to save file
#define ERR_NOMEM 3 // running out of memory...
#define HB_SIGNATURE ">>HeartBeat Session dump (C) LVA 1992\n"
//-------------------------------------------------------------------------
// Function Prototypes
// -------------------
struct MsgPort *CreateMsgPort (void);
void DeleteMsgPort (struct MsgPort*);
// Local function prototypes
//--------------------------
void cleanup (void);
void exit (int code);
void text_to_heap (struct FileCache *fc);
void refresh_counters (void);
void handle_quit (void);
void handle_activation (void);
void handle_menuselection (SHORT menu);
void handle_keypress (char key);
void handle_click (void);
void handle_gadgets (void);
void append_lib_menus (void);
void display_functions (void);
void handle_mousemove (void);
void unpatch_functions (void);
void find_function_info (void);
void popup_About_req (void);
void reset_counters (void);
void kill_taskReq_window (void);
void kill_regsReq_window (void);
void watch_task (int index);
void quick_msg (char *message, char *exit);
void strip_lf (char *str);
void collect_taskinfo (struct Node *task);
void CloseWindowSafely (struct Window *win);
void StripIntuiMessages (struct MsgPort *mp, struct Window *win);
void init_context (struct Context *cx);
void fill_box (int x, int y, int yoffs, int width, struct RastPort*, int pen);
#define highlight_box(x,y,w) fill_box(x,y,0,w,rp,3)
#define erase_box(x,y,w) fill_box(x,y,0,w,rp,0)
#define highlight_monbox(x,y,w) fill_box(x,y,12,w,monrp,3)
#define erase_monbox(x,y,w) fill_box(x,y,12,w,monrp,0)
BOOL init_program (void);
BOOL process_fd_file (struct FileCache *fc);
BOOL popup_funcwindow (void);
BOOL add_function (struct SysCall*);
BOOL ask_register_info (char *wintitle, struct Context *ctxt);
BOOL ask_task (char *wintitle);
int calc_num_fd_files (void);
int load_funclist (void);
int save_funclist (void);
char *get_filename (char * windowmsg);
struct SysCall *find_func (char *libname, char *funcname);
//-------------------------------------------------------------------------
// Global variables
//-----------------
extern struct ExecBase *SysBase;
extern struct IntuitionBase *IntuitionBase;
struct GadToolsBase *GadToolsBase; // not part of lc.lib this one
struct AslBase *AslBase;
extern struct GfxBase *GfxBase;
extern void WEDGE(void); // to be copied and patched
struct library {
struct List funcslist; // functions for this lib/dev/resource
char *libname; // name of this library/device/resource
struct Library *openhandle; // ret from OpenLibrary/OpenDevice/OpenResource
int numfuncs; // # of entry points
};
struct library libs[MAX_LIBS]; // an array to store info on all libs
struct library *curlib=NULL; // curlib points to the current library
struct SysCall *patched_calls[MAX_PATCHED]; // array of ptrs into patched funcs
struct SysCall *selected_syscall; // -> selected func for register template
struct SysCall *node_pool,*pool_base;
struct Context *ctxt_pool;
struct Task *SNOOP_TASK; // Task that's being monitored
struct List taskList; // global list header for LISTVIEW gadget
struct MsgPort *timerport,*IDCMPport; // our two main IPC ports
struct timerequest timereq;
struct Window *window, *monwindow, *taskwindow, *regswindow;
struct Screen *screen;
struct RastPort *rp,*monrp,*regsrp;
struct Menu *menustrip;
struct TextAttr *TAttr;
struct Gadget *taskReqGads; // ptr to gadlist for Task Requester window
struct Gadget *regsReqGads; // ptr to gadlist for Regs Requester window
void *vi; // global VisualInfo ptr for Workbench Screen
struct FileCache fd_caches[MAX_LIBS]; // bookkeeping for cached .fd files
struct FileCache FdDirCache; // for .fd directory listing file
int num_fdfiles; // number of .fd libraries (Constant)
int numlibs; // number of accessible libs (Constant)
int total_funcs; // total numer that we know of
int libnum; // index into library array (Variable)
int lowlibs; // # of libraries in A..I menu
int monitored; // # of functions monitored (= patched)
int app_state; // state application finds itself in...
char filename_buf[MAX_FILENAME]; // large buff to construct abs file names
struct RDAres { // array for ReadArgs()
ULONG forceopen; // force Library to be resident
ULONG verbose; // print lots of debug info.
} options;
#define VERBOSE options.verbose
#define FORCEOPEN options.forceopen
SHORT winwidth=WINWIDTH, winheight=WINHEIGHT;
SHORT maxrows; // # of rows to print functions
SHORT mousex,mousey;
SHORT msgcode;
SHORT oldboxx = -1, oldboxy = -1;
SHORT oldmonboxy = -1;
SHORT task_num; // "remember" variable for Task Req gadget
APTR IAddr; // ptr to Intuition object (Gadget,..)
BOOL quit_me; // IDCMP quit request
BOOL menus_exist = FALSE; // SetMenuStrip() not yet...
//-------------------------------------------------------------------------
// A NewWindow struct to open application Windows (re-used many times)
// (Initial values are for main function window only)
//-------------------------------------------------------------------------
struct NewWindow nw = {
0, 16, WINWIDTH, WINHEIGHT,
255, 255, /* Default pens */
// I want to know about following IDCMP Message types
// IDCMP_CLOSEWINDOW|
// IDCMP_ACTIVEWINDOW|
// IDCMP_MENUPICK|
// IDCMP_MOUSEMOVE|
// IDCMP_MOUSEBUTTONS|
// IDCMP_GADGETUP|
// LISTVIEWIDCMP|
// All windows we open get this set of IDCMP bits attached to them.
// Not all windows can generate all those types of msgs though, but that
// doesn't matter (Jimmy).
#define IDCMP_SELECTION (IDCMP_CLOSEWINDOW|\
IDCMP_MENUPICK|\
IDCMP_MOUSEMOVE|\
IDCMP_MOUSEBUTTONS|\
IDCMP_GADGETUP|\
LISTVIEWIDCMP|\
IDCMP_ACTIVEWINDOW)
// BUT we force Intuition to use our own MsgPort so specify 0 here
0,
// Standard window flags
WFLG_ACTIVATE|
WFLG_CLOSEGADGET|
WFLG_DEPTHGADGET|
WFLG_DRAGBAR|
WFLG_REPORTMOUSE,
NULL, // No gadgets in this window
(struct Image *) NULL,
"Amiga HeartBeat 1.0 (written by LVA © April 1992)", // Window title
(struct Screen *) NULL,
(struct BitMap *) NULL,
100, 40, /* Minimum sizes */
65535, 65535, /* Maximum sizes */
WBENCHSCREEN /* and put it on the workbench */
};
//-------------------------------------------------------------------------
// Menu Layout in compact GadTools format.
//-------------------------------------------------------------------------
#define PROJECT_MENU 0
#define OPTIONS_MENU 1
#define LIBS_1_MENU 2
#define LIBS_2_MENU 3
#define NEW_ITEM 0
#define LOAD_ITEM 1
#define SAVE_ITEM 2
#define DUMMY_ITEM0 3
#define ABOUT_ITEM 4
#define QUIT_ITEM 5
#define RESET_ITEM 0
#define GLOBAL_ITEM 1
#define TASK_ITEM 2
// First empty slot @ offset N
#define NEWMENU_APPEND 12
struct NewMenu mymenus[MAX_LIBS+NEWMENU_APPEND+2]= {
{ NM_TITLE, "Project", NULL, 0,0,0},
{ NM_ITEM, "New", "N", 0,0,0}, // Kill monitor window
{ NM_ITEM, "Load Session...", "L", 0,0,0}, // Load monitor window selection
{ NM_ITEM, "Save Session...", "S", 0,0,0}, // Save monitor window selection
{ NM_ITEM, NM_BARLABEL,NULL, 0,0,0},
{ NM_ITEM, "About...", NULL, 0,0,0},
{ NM_ITEM, "Quit", "Q", 0,0,0},
{ NM_TITLE, "Options", NULL, 0,0,0},
{ NM_ITEM, "Reset counters", "C",0,0,0},
{ NM_ITEM, "Watch GLOBAL", "G",0,0,0},
{ NM_ITEM, "Watch Task.....","T",0,0,0},
{ NM_TITLE, "Libraries A-I",NULL,0,0,0} };
// { NM_END, NULL, NULL, 0,0,0} };
char version[]="$VER: HeartBeat 1.0 ©LVA (19/MAY/94)";
/**************************************************************************
** Here's the main program.
**
** Basically:
** - load and parse all .fd files from the FD: directory.
** - bring up main function selection window (generate menustrip)
** - accumulate function selections in the "monitored functions" window
** - allow users to switch between global and Task snooping, set argument
** register traps, etc...
**
** The main event loop needs some explaining:
** We do a combined Wait() for two sources:
** 1) timer.device timeouts
** 2) Intuition IntuiMessages
** The timer.device is used instead of INTUITICKS because we want the counters
** output window to continue updating when another non-HeartBeat window becomes
** the active one. (INTUITICKS stop when your window is deactivated).
**
** All windows are hooked into the same message port which in turn generates
** (and uses) just one signal. That's why the code uses ModifyIDCMP() and
** messes around with the Window->UserPort field.
**
** When windows other than the main function window (the window listing all
** function in a particular library) are opened, input to other windows is
** not blocked (IDCMP loop still collects from all sources).
****************************************************************************/
void main (void) {
struct RDArgs *rdargs;
struct IntuiMessage *msg;
ULONG msgtype;
ULONG signals,timeout_signal,Intevent_signal;
if ( ((struct Library*)SysBase)->lib_Version < 36) {
printf("
HeartBeat
needs AmigaDOS 2.0 (V36+)...
Sorry!
\n");
exit(10);
}
// Check command line options.
if (! (rdargs = ReadArgs("FORCE/S,VERBOSE/S", (ULONG*) &options, NULL))) {
printf("Amiga HeartBeat written by Laurence Vanhelsuwé © April 1992\n");
printf("HeartBeat is a system call monitoring utility.\n");
printf("Type HART ? for a full syntax template.\n");
exit(5);
}
FreeArgs(rdargs);
// Open libraries, init arrays, memory pools, etc...
if (!init_program()) exit(50);
// From the standard .fd (function definitions) files, get all the info
// about system calls that we need.
find_function_info();
// bring up main function selection window with its menus
if (!popup_funcwindow()) {
printf("Failed to open main window (need at least %d*%d Workbench)\n",
WINWIDTH, WINHEIGHT+16);
cleanup();
}
curlib = &libs[0]; // curlib global for further references
display_functions(); // 1st library is default
// cache the two possible signals that we can receive
timeout_signal = 1<<timerport->mp_SigBit;
Intevent_signal = 1<<IDCMPport->mp_SigBit;
// Send initial timeout request to timer.device
// (provides refresh interrupts)
timereq.tr_time.tv_secs = 0;
timereq.tr_time.tv_micro = 1000000/REFRESH_FREQ; // MICRO != MILLI
timereq.tr_node.io_Command = TR_ADDREQUEST;
SendIO((struct IORequest *) &timereq); // asynchronous IO !
quit_me = FALSE; // user hasn't clicked on CLOSE gadget yet...
while (!quit_me) {
// Wait for an Intuition event or a Timer device timeout (or both simult.)
signals = Wait( Intevent_signal | timeout_signal);
if (signals & timeout_signal) { // .... ping !
GetMsg(timerport); // dequeue msg (is just an ACK)
timereq.tr_time.tv_secs = 0;
timereq.tr_time.tv_micro = 1000000/REFRESH_FREQ;
timereq.tr_node.io_Command = TR_ADDREQUEST;
timereq.tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
SendIO((struct IORequest *) &timereq); // ... pong !
refresh_counters(); // refresh counters window
}
// For Intuition Messages use the gadtools GetMsg/ReplyMsg variants
// so that gadtools can intercept gadget clicks which it (and not we)
// should process (e.g. for CYCLE_KIND or LISTVIEW_KIND gadgets !).
if (signals & Intevent_signal) {
while (msg = GT_GetIMsg(IDCMPport)) {
msgtype = msg->Class; // copy Message fields and
msgcode = msg->Code;
mousex = msg->MouseX;
mousey = msg->MouseY;
IAddr = msg->IAddress;
GT_ReplyIMsg(msg); // reply swiftly
#ifdef DEBUGX
printf("IDCMP MSGTYPE:%8lx (CODE:%8lx) ", msgtype, msgcode);
switch (msgtype) {
case CLOSEWINDOW: printf("CLOSEWINDOW\n"); break;
case ACTIVEWINDOW: printf("ACTIVEWINDOW\n"); break;
case MENUPICK: printf("MENUPICK:\n"); break;
case MOUSEMOVE: printf("MOUSEMOVE:\n"); break;
case MOUSEBUTTONS: printf("MOUSEBUTTONS\n"); break;
case VANILLAKEY: printf("VANILLAKEY\n"); break;
case GADGETUP: printf("GADGETUP:\n"); break;
case INTUITICKS: printf("INTUITICKS\n"); break;
case REFRESHWINDOW: printf("REFRESHWINDOW\n"); break;
default: printf("-- UNKNOWN --\n");
}
#endif
switch (msgtype) {
case CLOSEWINDOW: handle_quit(); break;
case ACTIVEWINDOW: handle_activation(); break;
case MENUPICK: handle_menuselection(msgcode); break;
case MOUSEMOVE: handle_mousemove(); break;
case MOUSEBUTTONS: handle_click(); break;
case VANILLAKEY: handle_keypress( (char) msgcode); break;
case GADGETUP: handle_gadgets(); break;
case INTUITICKS: break;
case REFRESHWINDOW: GT_BeginRefresh((struct Window*)IAddr);
GT_EndRefresh((struct Window*)IAddr, TRUE);
break;
default:
printf("Unknown IDCMP MSGTYPE:%x (CODE:%x)\n", msgtype, msgcode);
}
} // WHILE MESSAGES FROM A WINDOW
} // IF Intuition event...
} // WHILE NOT QUITTING
// At this stage a timout is ALWAYS outstanding so wait for it to come back.
WaitPort(timerport); GetMsg(timerport);
cleanup(); // finally before returning to the CLI...
}
//-------------------------------------------------------------------------
// Here follows the various IntuiMessage handlers.
// The handlers themselves further branch to the required logic depending
// on the "state" of HeartBeat. This state is usually directly related to
// which HeartBeat window is currently active.
//
// The IntuiMessage ACTIVEWINDOW therefore is the main cause of state switching.
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
// We received a CLOSEWINDOW event. This can come from any of our windows,
// so close requested window only (if main window: signal kill application).
//-------------------------------------------------------------------------
void handle_quit(void) {
switch (app_state) {
//-----------------------------------------------------------------
case FUNCWINDOW:
if (!monwindow && !taskwindow && !regswindow)
quit_me = TRUE;
break;
//-----------------------------------------------------------------
case MONWINDOW:
unpatch_functions(); // unpatch from system !
CloseWindowSafely(monwindow); monwindow = NULL;
if (curlib) display_functions(); // refresh func names list
// app_state = FUNCWINDOW; break;
ActivateWindow(window); // this is safer way to achieve same
break;
//-----------------------------------------------------------------
case REGSWINDOW:
kill_regsReq_window(); // done with window: get rid of it
// app_state = FUNCWINDOW; // revert to neutral state
ActivateWindow(window); // this is safer way to achieve same
break;
//-----------------------------------------------------------------
case TASKWINDOW:
kill_taskReq_window(); // done with window: get rid of it
// app_state = FUNCWINDOW; // revert to neutral state
ActivateWindow(window); // this is safer way to achieve same
break;
//-----------------------------------------------------------------
default: printf("QUIT BUG: appi in unknown state %d\n", app_state);
}
}
//-------------------------------------------------------------------------
// An ACTIVEWINDOW event has arrived at our main IDCMP comms port.
// To aid in determining which Gadgets get clicked or which window receives
// a mouse click, we track which window is currently the actived one.
//-------------------------------------------------------------------------
void handle_activation(void) {
switch (app_state) {
case FUNCWINDOW:
case MONWINDOW:
case TASKWINDOW:
case REGSWINDOW:
if (IAddr == (APTR) window) { app_state = FUNCWINDOW;} else
if (IAddr == (APTR) monwindow) { app_state = MONWINDOW; } else
if (IAddr == (APTR) taskwindow) { app_state = TASKWINDOW;} else
if (IAddr == (APTR) regswindow) { app_state = REGSWINDOW;}
break;
default: printf("ACTIV BUG: appi in unknown state %d\n", app_state);
}
}
//-------------------------------------------------------------------------
// A MENUPICK event has arrived. Only the main function window can generate
// this, but other windows might also be around so watch out for that.
//-------------------------------------------------------------------------
void handle_menuselection(SHORT menu) {
int library;
int err;
switch (MENUNUM(menu)) {
//----------------------------------------------------------------
case PROJECT_MENU:
switch (ITEMNUM(menu)) {
//--------------------------------------------------------
case NEW_ITEM:
if (!monwindow) break; // only if monitor window exists
unpatch_functions(); // unpatch from system !
CloseWindowSafely(monwindow); monwindow = NULL;
break;
//--------------------------------------------------------
case LOAD_ITEM:
err = load_funclist();
switch (err) {
case ERR_OK:
break;
case ERR_BADFILE:
quick_msg("Your selected file isn't a HeartBeat file!","Oopps..");
break;
case ERR_DOSERR:
quick_msg("Could not open your file!","Hmmm..");
break;
default:
quick_msg("**!! UNKNOWN ERROR IN LOAD_ITEM","arggh..");
break;
}
break;
//--------------------------------------------------------
case SAVE_ITEM:
if (monwindow) {
err = save_funclist();
switch (err) {
case ERR_OK:
break;
case ERR_DOSERR:
quick_msg("Couldn't save session!","Pitty");
break;
default:
quick_msg("**!! UNKNOWN ERROR IN LOAD_ITEM","arggh..");
break;
}
} else {
quick_msg("No monitoring session to save!","Sorry..");
}
break;
//--------------------------------------------------------
case ABOUT_ITEM:
popup_About_req();
break;
//--------------------------------------------------------
case QUIT_ITEM:
if (!monwindow && !taskwindow && !regswindow)
quit_me = TRUE;
break;
}
break;
//----------------------------------------------------------------
case OPTIONS_MENU:
switch (ITEMNUM(menu)) {
//--------------------------------------------------------
case GLOBAL_ITEM:
reset_counters();
SNOOP_TASK = NULL;
if (monwindow)
SetWindowTitles(monwindow,"Monitored functions (GLOBAL)", (char*) -1);
break;
//--------------------------------------------------------
case TASK_ITEM:
if (!taskwindow) {
// pop up window with list of Tasks. User selection
// will come to us via standard gadget clicks, not
// here !
ask_task("Please select any Task.");
} else {
WindowToFront(taskwindow);
ActivateWindow(taskwindow);
}
break;
//--------------------------------------------------------
case RESET_ITEM:
reset_counters();
break;
}
break;
//----------------------------------------------------------------
case LIBS_1_MENU:
library = ITEMNUM(menu);
curlib = &libs[library]; // curlib global for further references
display_functions();
break;
//----------------------------------------------------------------
case LIBS_2_MENU:
library = ITEMNUM(menu) + lowlibs;
curlib = &libs[library];
display_functions();
break;
}
}
//-------------------------------------------------------------------------
// If mouse points at a new function, de-highlight old box and highlight new
// one.
// This has the effect of a cursor over the available functions.
//-------------------------------------------------------------------------
void handle_mousemove(void) {
SHORT boxx,boxy,monboxy;
switch (app_state) {
//----------------------------------------------------------------
case MONWINDOW:
monboxy = (mousey-PANE_Y-1-12)/CHAR_HEIGHT;
// If mouse moved out of previous box area...
if (monboxy != oldmonboxy) {
// restrict legal box movements!
if ( monboxy >= 0 && monboxy < monitored ) {
if (oldmonboxy != -1) {
erase_monbox(0,oldmonboxy,(MAX_LABLEN+25)*CHAR_WIDTH);
}
highlight_monbox(0,monboxy,(MAX_LABLEN+25)*CHAR_WIDTH);
oldmonboxy = monboxy;
}
}
break;
//----------------------------------------------------------------
case FUNCWINDOW:
boxx = (mousex-PANE_X)/(MAX_LABLEN*CHAR_WIDTH);
boxy = (mousey-PANE_Y-1)/CHAR_HEIGHT;
// If mouse moved out of previous box area...
if (boxx != oldboxx || boxy != oldboxy) {
if (boxx >= 0 && // restrict legal box movements!
boxy >= 0 &&
boxy < maxrows &&
(boxy + maxrows*boxx) < curlib->numfuncs) {
if (oldboxx != -1) {
erase_box(oldboxx,oldboxy,(MAX_LABLEN*CHAR_WIDTH)-10);
}
highlight_box(boxx,boxy, (MAX_LABLEN*CHAR_WIDTH)-10 );
oldboxx = boxx; oldboxy = boxy;
}
}
break;
//----------------------------------------------------------------
}
}
//-------------------------------------------------------------------------
// A MOUSEBUTTON event arrived.
//
// If in function window then add selected function to list of traced ones if
// not already being traced.
//
// If in monitored functions window, see comment further down.
//-------------------------------------------------------------------------
void handle_click (void) {
if (msgcode & 0x80) return; // don't bother with release clicks
switch (app_state) {
//----------------------------------------------------------------
case FUNCWINDOW:
if (oldboxx != -1) { // don't select invalid funcs
struct SysCall *syscall; // define some very local variables
int funcnum;
funcnum = oldboxy + maxrows * oldboxx;
syscall = ((struct SysCall *)curlib->funcslist.lh_Head) + funcnum;
// add function to list of tracked ones
add_function(syscall);
// Once at least one function is being traced,
// the option menu items become available
OnMenu(window, FULLMENUNUM(OPTIONS_MENU,NOITEM,NOSUB));
}
break;
//----------------------------------------------------------------
// A click in the monitoring window can mean two things:
// a) user wants to define the input registers for a match
// b) if the selected function has trapped and frozen its Task
// un-freeze task by sending it a Signal()
//----------------------------------------------------------------
case MONWINDOW:
if (oldmonboxy != -1) { // don't accept invalid funcs
struct Context *cx;
cx = patched_calls[oldmonboxy]->ctxt;
if (SNOOP_TASK && cx->frozen) {
Signal(SNOOP_TASK, 1<<24); // de-freeze Task
} else {
selected_syscall = patched_calls[oldmonboxy];
if (!regswindow) {
// pop up window with all 680x0 registers. User changes
// will come to us via standard gadget clicks, not here !
ask_register_info("Define Arg Registers", selected_syscall->ctxt);
} else {
WindowToFront(regswindow);
ActivateWindow(regswindow);
}
}
}
break;
//----------------------------------------------------------------
case TASKWINDOW:
case REGSWINDOW: break;
default:
printf("CLICK BUG: appi in unknown state %d\n", app_state);
}
}
//-------------------------------------------------------------------------
// **!! At this point HartBeat doesn't use individual key presses.
//-------------------------------------------------------------------------
void handle_keypress(char key) {
// printf("Key pressed: %c\n", key);
}
//-------------------------------------------------------------------------
// A GADGETUP event arrived. Meaning any of our Gadgets scattered around
// multiple Windows has been clicked.
//-------------------------------------------------------------------------
#define TSKLST_LIST 0
#define TSKLST_USE 1
#define TSKLST_CANCEL 2
#define REGLST_CYCLE 32
#define REGLST_USE 33
#define REGLST_CANCEL 34
void handle_gadgets(void) {
USHORT gid;
gid = ((struct Gadget*)IAddr)->GadgetID;
switch (app_state) {
//-----------------------------------------------------------------
case REGSWINDOW:
switch (gid) {
//---------------------------------------------------------
case REGLST_USE: {
struct Gadget *gad;
struct Context *ctxt;
char *str;
int regs,val;
// -> 1st string Gadget (Skip Gadtools "context" gadget)
gad = regsReqGads->NextGadget; ctxt = selected_syscall->ctxt;
for(regs=0; regs<32; regs++) {
str = ((struct StringInfo*)gad->SpecialInfo)->Buffer;
if (*str == '$') {
if (!sscanf(str+1,"%lx", &val)) val = -1;
} else {
if (!sscanf(str,"%ld", &val)) val = -1;
}
if (regs&1) // if gadID is odd,
ctxt->regmasks[regs>>1] = val; // it's a mask
else // else
ctxt->regvals[regs>>1] = val; // it's a register
gad = gad->NextGadget; // goto next string gadg
} // end of for
} // end of compound statement
// Fall thru to kill window
//---------------------------------------------------------
case REGLST_CANCEL:
kill_regsReq_window(); // done with window: get rid of it
// app_state = FUNCWINDOW; // revert to neutral state
ActivateWindow(window); // this is safer way to achieve same
break;
//---------------------------------------------------------
case REGLST_CYCLE:
selected_syscall->ctxt->freeze = (msgcode==1) ? -1 : 0 ;
selected_syscall->ctxt->frozen = FALSE;
break;
//---------------------------------------------------------
}
break;
//-----------------------------------------------------------------
case TASKWINDOW:
switch ( ((struct Gadget*)IAddr)->GadgetID) {
//---------------------------------------------------------
case TSKLST_LIST:
task_num = msgcode; // register which Task user selected
break;
//---------------------------------------------------------
case TSKLST_USE:
watch_task(task_num); // using previously recorded selection
SetWindowTitles(monwindow,"Monitored functions (TASK)", (char*) -1);
reset_counters(); // switch to snoop new Task
// Fall thru to kill window
//---------------------------------------------------------
case TSKLST_CANCEL:
kill_taskReq_window(); // done with window: get rid of it
// app_state = FUNCWINDOW; // revert to neutral state
ActivateWindow(window); // this is safer way to achieve same
break;
}
break;
//-----------------------------------------------------------------
case FUNCWINDOW:
case MONWINDOW:
default: printf("GADGET BUG: appi in unknown state %d\n", app_state);
}
}
//-------------------------------------------------------------------------
// List all functions of a selected library in neat columns in the main window.
// Display all functions which don't point to ROM in INVERSE VIDEO.
//-------------------------------------------------------------------------
void display_functions(void) {
int i,len;
struct SysCall *func;
ULONG vector;
SetDrMd(rp,JAM1); // normal drawing mode
SetAPen(rp,0); // use background pen to wipe
RectFill(rp, PANE_X, PANE_Y, winwidth-5, winheight-3); // previous functions off
SetAPen(rp,1); // use text pen
maxrows = (winheight-18)/CHAR_HEIGHT;
// point to first function node
func = (struct SysCall*) curlib->funcslist.lh_Head;
i = 0; // start index from 0
while (func->func_node.ln_Succ) {
// limit label length to fit in column
len = (func->namelen > MAX_LABLEN ? MAX_LABLEN : func->namelen);
Move(rp, PANE_X + (i/maxrows)*MAX_LABLEN*CHAR_WIDTH,
PANE_Y + 6 +(i%maxrows)*CHAR_HEIGHT);
// find out where function points to currently.
// if not the ROM then highlight function name.
vector = *((ULONG*)(((char*)func->parent_lib) + func->func_LVO +2));
if (vector < 0xF80000 || vector >0xFFFFFC) {
SetDrMd(rp,INVERSVID); // highlight function
Text(rp, func->funcname, len);
SetDrMd(rp,JAM1); // revert to normal mode
} else {
Text(rp, func->funcname, len);
}
func = (struct SysCall*) func->func_node.ln_Succ;
i++;
}
app_state = FUNCWINDOW; // functions are now selectable...
oldboxx = -1; // invalidate last box
}
//-------------------------------------------------------------------------
// User selected the "About..." option in the Project menu.
// Give him some info.
//-------------------------------------------------------------------------
void popup_About_req(void) {
static struct EasyStruct aboutES = {
sizeof (struct EasyStruct),
0,
"HeartBeat Program Information Broadcast", "\
HeartBeat allows you to snoop on any Amiga system calls.\n\
To select functions to be tracked, first select any Library,\n\
Device or Resource from the Libraries menus and then click\n\
on any function(s) you wish to snoop on.\n\
WARNING: Take it easy with Exec... or be punished !\n\
e.g. SuperVisor(), SumLibrary(), CacheClearU(), ...\n\
\n\
HeartBeat was written by Laurence Vanhelsuwé.\n\
You can contact the author at the following address:\n\
\n\
Christinastraat 105\n\
B-8400 Oostende\n\
Belgium\n\
Europe",
"Thanks|Great|WILLCO|Yeah|OK",
};
EasyRequest(window, &aboutES, NULL);
}
//-------------------------------------------------------------------------
// User wants to load a previously saved monitoring session.
//-------------------------------------------------------------------------
#define BUFLEN 60 // size of line buffer
int load_funclist (void) {
struct SysCall *scall;
char libnamebuf[BUFLEN],funcnamebuf[BUFLEN];
char *fname, *ptr;
FILE *fhandle;
BOOL added;
// Ask user where to load session information from.
fname = get_filename("Session filename to LOAD");
if (!fname) return ERR_NOMEM;
fhandle = fopen(fname,"r"); if (!fhandle) return ERR_DOSERR;
// Check that file is a HeartBeat file.
fgets(libnamebuf, BUFLEN, fhandle);
if (strcmp(libnamebuf, HB_SIGNATURE)) {
fclose(fhandle);
return ERR_BADFILE;
}
// Read in session file until EOF is encountered or until func list is full
do {
ptr = fgets(libnamebuf ,BUFLEN, fhandle);
ptr = fgets(funcnamebuf,BUFLEN, fhandle);
if (ptr) {
scall = find_func(libnamebuf, funcnamebuf);
if (scall) {
added = add_function(scall);
} else {
// printf("%s, %s NOT VISISBLE !\n", libnamebuf, funcnamebuf);
}
}
} while (ptr && added);
fclose(fhandle);
// Once at least one function is being traced,
// the option menu items can become available
OnMenu(window, FULLMENUNUM(OPTIONS_MENU,NOITEM,NOSUB));
return ERR_OK;
}
//-------------------------------------------------------------------------
// Check whether a function traced in a previous session is again visible.
// If so, return its SysCall struct ptr.
//-------------------------------------------------------------------------
struct SysCall *find_func( char *libname, char *funcname) {
struct SysCall *syscall;
strip_lf(libname); strip_lf(funcname);
for (libnum=0; libnum<numlibs; libnum++) { // chk all lib slots
if (libs[libnum].openhandle) { // if loaded..
if (!strcmp(libs[libnum].libname, libname)) { // and same as arg
syscall = (struct SysCall*) libs[libnum].funcslist.lh_Head;
while (syscall->func_node.ln_Succ) { // traverse func list
if (!strcmp (syscall->funcname, funcname)) // if func exists
return syscall; // tell caller
syscall = (struct SysCall*) syscall->func_node.ln_Succ;
} // while functions on library function list
} // only if same library as argument
} // only for loaded libraries
} // while libraries
return NULL;
}
//-------------------------------------------------------------------------
// User wants to save his current monitoring sessions for later.
//-------------------------------------------------------------------------
int save_funclist (void) {
struct SysCall **syscall = patched_calls; // point to array of SysCall ptrs
char *fname;
int i;
FILE *fhandle;
// Ask user where to save session information to.
fname = get_filename("Session filename to SAVE");
if (!fname) return ERR_NOMEM;
// Open file and print file header
fhandle = fopen(fname,"w");
fprintf(fhandle, HB_SIGNATURE);
// Dump session information to file
for(i=1; i <= monitored; i++) {
fprintf(fhandle, "%s\n", (*syscall)->parent_lib->lib_Node.ln_Name);
fprintf(fhandle, "%s\n", (*syscall)->funcname);
syscall++; // goto next SysCall structure
}
fclose(fhandle);
return ERR_OK;
}
//-------------------------------------------------------------------------
// Generic Filename request.
//-------------------------------------------------------------------------
char * get_filename(char * windowmsg) {
struct FileRequester *fr;
char *ptr;
BOOL result;
fr = AllocAslRequestTags(ASL_FileRequest, NULL);
if (!fr) return NULL;
// Ask user what filename he wants to use
result = AslRequestTags(fr,
// ASLFR_TitleText,windowmsg, // need a newer INCLUDE file : AARRGH
ASL_Hail,windowmsg, // using obsolete version TAG instead **!!
NULL);
if (!result) return NULL;
if (fr->rf_Dir) { // if there's a directory component..
ptr = stpcpy(filename_buf, fr->rf_Dir); // 1st copy path
if (*(ptr-1) != ':')
*ptr++ = '/'; // separate path from file
stpcpy(ptr, fr->rf_File); // then append filename
} else {
strcpy(filename_buf, fr->rf_File);
}
FreeAslRequest(fr);
return filename_buf;
}
//-------------------------------------------------------------------------
// This is a generic routine to bring up small message requesters.
//-------------------------------------------------------------------------
void quick_msg (char *message, char *exit) {
struct EasyStruct quickES;
quickES.es_StructSize = sizeof (struct EasyStruct);
quickES.es_Flags = 0;
quickES.es_Title = "HeartBeat says...";
quickES.es_TextFormat = message;
quickES.es_GadgetFormat = exit;
EasyRequest(window, &quickES, NULL);
}
//-------------------------------------------------------------------------
// Most Amiga systems should have a directory full of ".fd" files containing
// Amiga system function definitions.
// Go and find this directory and extract as much possible information from
// these files.
//
// An example of real .FD file follows
/*
* "battclock.resource"
##base _BattClockBase
##bias 6
##public
ResetBattClock()()
ReadBattClock()()
WriteBattClock(time)(d0)
##private
battclockPrivate1()()
battclockPrivate2()()
##end
*/
//-------------------------------------------------------------------------
void find_function_info(void) {
struct FileCache *fc;
char *fname;
int i;
// Find out how many .fd files in FD: directory
num_fdfiles = calc_num_fd_files();
if (VERBOSE) printf("FD: directory contains %d libraries.\n", num_fdfiles);
if (num_fdfiles > MAX_LIBS) {
printf("Your FD: Directory contains too many entries for HeartBeat (%d > %d)\n",
num_fdfiles, MAX_LIBS);
cleanup();
}
// Move to FD: directory so we can use short relative filanemes.
chdir("FD:");
fc = fd_caches; // starting from 1st .fd file
fname = FdDirCache.filebuf;
total_funcs = libnum = 0; // starting from library array slot #0
for (i=0; i< num_fdfiles; i++) {
if (!load_file(fname, fc)) {
if (VERBOSE) printf("Couldn't cache %s !\n", fname);
cleanup();
}
fc->filename = fname; // store name of cached file
// parse .FD file and extract all function call information
if (!process_fd_file(fc)) {
if (VERBOSE) printf("File %s couldn't be parsed.\n", fc->filename);
} else {
libnum++; // done one more library...
}
fc++; // point to next file cache
fname += 1+ strlen(fname); // point to next .fd filename
}
if (VERBOSE)
printf("Counted %d functions in %d libraries\n", total_funcs, libnum);
// from now on, the remainder of the node pool is used to allocate
// function contexts. (node_pool is used to reset the ctxt_pool ptr).
ctxt_pool = (void*) node_pool;
// set the library array index ceiling
numlibs = libnum;
}
//-------------------------------------------------------------------------
// Find howmany .fd files there are in the FD: directory.
//-------------------------------------------------------------------------
int calc_num_fd_files( void ) {
int match;
char *matchptr; // ptr to be filled in by stcpm()
register int lines;
register char * ptr;
BOOL ret;
// Let "LIST" do all the hard work.
ret = Execute("C:LIST >T:FDLIST FD:#?.fd nohead quick",0,0);
if (!ret) {
printf("Could not 'LIST' your FD: directory.\n");
cleanup();
}
// Let "SORT" do some more hard work.
ret = Execute("C:SORT T:FDLIST TO T:FDL2",0,0);
if (!ret) {
printf("Could not 'SORT' filenames.\n");
cleanup();
}
if (! load_file("T:FDL2", &FdDirCache)) {
printf("Failed to load LIST output 'T:FDL2'\n");
cleanup();
}
ptr = FdDirCache.filebuf; // go analyze LIST's output.
lines = 0;
while (*ptr) { // while not EOF
if (*ptr++ == LF) { // count lines
lines++;
}
}
// Turn filename list into list of C-strings
text_to_heap (&FdDirCache);
// Now check first line and make sure it contains the substring ".fd"
// If not, then list probably printed an error message, so we quit.
// do an unanchored pattern match (Lattice function)
ptr = FdDirCache.filebuf;
match = stcpm(ptr, ".fd",&matchptr);
if (!match) {
printf("LIST of FD: directory failed.\n");
cleanup();
}
// if LIST went OK then # of lines == # of .fd files
return lines;
}
//-------------------------------------------------------------------------
// User just clicked the currently highlighted system function name.
// Add this system call to the list of traced ones and stretch Monitoring
// window to hold extra function line.
//
// return FALSE if we can't accept any more functions.
//-------------------------------------------------------------------------
BOOL add_function (struct SysCall *syscall) {
char addr_str[]="$0000FFFF "; // output buf for bin2hex conversion
short oldheight;
struct NewWindow *NW;
struct Context *cx;
register USHORT *wedge,*code;
APTR newpatch;
int dy,len;
register int i;
if (syscall->ctxt) return TRUE; // don't bother patching same func twice!!
if (monitored == MAX_PATCHED) return FALSE; // or exceeding max number
// If function call is the very first one and Window therefore does
// not exist yet, create it first.
if (!monitored) {
NW = &nw;
NW->Title = "Monitored Functions";
NW->TopEdge = 30; NW->LeftEdge = 640-380;
NW->Width = PANE_X+48*CHAR_WIDTH; NW->Height = 27;
NW->Flags = WFLG_CLOSEGADGET|
WFLG_DEPTHGADGET|
WFLG_REPORTMOUSE|
WFLG_DRAGBAR;
monwindow = OpenWindow(NW); if (!monwindow) return FALSE;
monwindow->UserPort = IDCMPport; // share global event MsgPort
ModifyIDCMP(monwindow, IDCMP_SELECTION); // allow events from new window
monrp = monwindow->RPort;
Move (monrp, PANE_X, PANE_Y+6);
SetDrMd(monrp, JAM1); SetAPen(monrp, 1);
//Text (monrp, " Called Matched Function", 30);
// "$0000FFFF $0000FFFF SuperVisor"
Text (monrp, " Called Matched Address Function", 35);
// "$00FFFF $00FFFF $07FFFFFF SuperVisor"
Move (monrp, PANE_X, PANE_Y+8);
Draw (monrp, PANE_X+35*CHAR_WIDTH , PANE_Y+8);
}
oldheight = monwindow->Height;
SizeWindow(monwindow, 0, CHAR_HEIGHT);
Delay(7); // Wait for Intuition to actually do the async sizing
// Check that window actually stretched correctly.
// If it didn't (coz window's touching bottom of Workbench) then
// move window up and try again (If it fails again: quit)
dy = monwindow->Height - oldheight;
if (dy != CHAR_HEIGHT) {
oldheight = monwindow->Height;
MoveWindow(monwindow, 0, -(CHAR_HEIGHT-dy));
SizeWindow(monwindow, 0, CHAR_HEIGHT-dy);
Delay(7);
if (oldheight != (monwindow->Height -(CHAR_HEIGHT-dy))) return FALSE;
}
// OK. At this point we can definitely patch this function.
// Print function information in window. (previous Function address printing
// is done lower down after SetFunction() )
SetDrMd(monrp, JAM1); SetAPen(monrp, 1);
Move(monrp, PANE_X + (27*CHAR_WIDTH), PANE_Y +10 + (monitored+1) * CHAR_HEIGHT);
Text(monrp, syscall->funcname, syscall->namelen);
patched_calls[monitored++] = syscall; // track call in array of patched ones
cx = ctxt_pool++; // allocate a Context
if ( ((char*)ctxt_pool) > ((char*)pool_base)+POOL_SIZE-200)
printf("About to blow pool ! **BUG**\n");
init_context(cx); // fill in regs, zero counters
syscall->ctxt = cx; // give SysCall struct a Context
wedge = (USHORT*) ctxt_pool; // spot for wedge code
newpatch = (APTR) wedge; // remember start address
code = (USHORT*) WEDGE; // R/O image of wedge code
i = *(code-1); // is this many WORDs long
while (i--) {
*wedge++ = *code++; // Copy wedge routine
}
ctxt_pool = (struct Context*) wedge; // update free memory ptr
// Now patch library and fill in normal func address and counter address
// in patch.
Disable();
cx->stdvector = SetFunction(syscall->parent_lib, syscall->func_LVO, newpatch);
#define patch_wedge(wedge,offs,patch) \
*((ULONG*)((wedge)+ *(((UWORD*)WEDGE)-offs))) = (ULONG)patch;
patch_wedge((UWORD*)newpatch, 2, cx->stdvector); // JMP CHAIN
patch_wedge((UWORD*)newpatch, 3, &cx->trapped_cnt); // MATCH CNT
patch_wedge((UWORD*)newpatch, 4, &cx->called_cnt); // NORM CNT
patch_wedge((UWORD*)newpatch, 5, cx); // CTXT PTR
Enable();
// generate hex string of function code start address
len = stcl_h(addr_str+1, (long) cx->stdvector);
Move(monrp, PANE_X + ((24-len)*CHAR_WIDTH), PANE_Y +10 + monitored*CHAR_HEIGHT);
Text(monrp, addr_str, len+1);
return TRUE; // function successfully added to list
}
//-------------------------------------------------------------------------
// Initialize a Context structure.
// Fill registers with a no-match template and clear counters.
//-------------------------------------------------------------------------
void init_context( struct Context *cx) {
register int i;
// D0 and A7 get special values to get fastest possible "no match"
cx->regvals[0] = 0xA1B2C3D4; cx->regvals[15] = 0x00000000;
cx->regmasks[0]= 0xFFFFFFFF; cx->regmasks[15]= 0xFFFFFFFF;
for (i=1; i<15; i++) { // skipping D0 and A7 itself
cx->regvals[i] = 0; // clear interface regs
cx->regmasks[i] = 0;
}
cx->freeze = cx->frozen = FALSE; // don't freeze on match
cx->called_cnt = cx->trapped_cnt = 0; // clear counters
cx->last_called_cnt = cx->last_trapped_cnt = -1; // force first update
}
//-------------------------------------------------------------------------
// For all patched system functions, see if their call frequency counters
// have changed since last time and if so re-print them.
//-------------------------------------------------------------------------
void refresh_counters(void) {
char counter_str[]="$0000FFFF "; // output buf for bin2hex conversion
register struct Context *cx;
struct SysCall **syscall;
UBYTE oldmask;
int i;
register int len;
if (!monwindow) return; // update monitor window if it exists
oldmask = monrp->Mask; monrp->Mask = 1; // write counters in plane 1 only
SetDrMd(monrp, JAM2); SetAPen(monrp, 1); // overwrite mode
syscall = patched_calls; // point to array of SysCall ptrs
for(i=1; i <= monitored; i++) { // for all monitored funcs DO...
cx = (*syscall)->ctxt;
if (cx->called_cnt != cx->last_called_cnt) { // if counter changed
cx->last_called_cnt = cx->called_cnt; // old = new
len = stcl_h(counter_str+1, cx->called_cnt); // gen hex
len = len > 6 ? 6 : len;
Move(monrp, PANE_X + (6-len)*CHAR_WIDTH, PANE_Y+10 + i*CHAR_HEIGHT);
Text(monrp, counter_str, len+1); // print counter
}
if (cx->trapped_cnt != cx->last_trapped_cnt) {
cx->last_trapped_cnt = cx->trapped_cnt;
len = stcl_h(counter_str+1, cx->trapped_cnt);
len = len > 6 ? 6 : len;
Move(monrp, PANE_X + ((8+6)-len)*CHAR_WIDTH, PANE_Y+10 + i*CHAR_HEIGHT);
Text(monrp, counter_str, len+1); // print counter
}
Move(monrp, PANE_X + (26*CHAR_WIDTH), PANE_Y+10 +i*CHAR_HEIGHT);
if (cx->freeze) {
if (cx->frozen) {
Text(monrp, "*", 1);
} else {
Text(monrp, "!", 1);
}
} else {
Text(monrp, " ", 1);
}
syscall++; // goto next SysCall structure
}
monrp->Mask = oldmask; // restore normal bitplane writing mask
}
//-------------------------------------------------------------------------
// User has switched call tracking mode (global <-> Task only)
// so reset all call counters to start off with clean statistics
//-------------------------------------------------------------------------
void reset_counters(void) {
struct library *lib;
register struct SysCall *syscall;
UBYTE oldmask;
if (monitored) { // clear old counter strings
if (!monwindow) printf("BUG in reset_counters!\n");
oldmask = monrp->Mask; monrp->Mask = 1; // Affect 1st bitplane only
SetAPen(monrp, 0);
RectFill(monrp, PANE_X, PANE_Y+12,
PANE_X+ 15*CHAR_WIDTH -1, PANE_Y+12+ monitored * CHAR_HEIGHT);
monrp->Mask = oldmask; // restore normal bitplane mask
}
for (libnum=0; libnum<numlibs; libnum++) {
if (libs[libnum].openhandle) { // only if library valid
lib = &libs[libnum];
syscall = (struct SysCall*) lib->funcslist.lh_Head;
while (syscall->func_node.ln_Succ) {
if (syscall->ctxt) { // only patched functions have counters...
init_context(syscall->ctxt);
}
syscall = (struct SysCall*) syscall->func_node.ln_Succ;
} // while functions in library
} // if valid library
} // for all slots in libs array
}
//-------------------------------------------------------------------------
// Highlight a rectangle at BOX coordinates x,y that's width wide.
//-------------------------------------------------------------------------
void fill_box( int x, int y, int yoffs, int width, struct RastPort *rp, int pen) {
int ulx,uly;
UBYTE oldmask;
ulx = PANE_X + x * MAX_LABLEN*CHAR_WIDTH;
uly = PANE_Y + yoffs+ y * CHAR_HEIGHT;
SetDrMd(rp, JAM1); SetAPen(rp, pen);
oldmask = rp->Mask; rp->Mask = 2; // Affect 2nd bitplane only
RectFill(rp, ulx, uly, ulx+width, uly+CHAR_HEIGHT-1);
rp->Mask = oldmask; // restore normal bitplane mask
}
//-------------------------------------------------------------------------
// Analyze a cached .fd file and extract system call information.
// Open the Library/Device/Resource of this file to get a firm grip on object.
//-------------------------------------------------------------------------
BOOL process_fd_file (struct FileCache *fc) {
struct SysCall *funky; // a ptr to a new System Call "node"
struct Node *object=NULL;
char *endquote,*endfunc;
char *objtype,*newptr;
register char *ptr; // .fd file text scanning ptr
BOOL skip_private = FALSE; // skip functions which are system private
int scan_state = NEED_NAME; // starting scanning state
int bias = -6; // default starting LVO
libs[libnum].numfuncs = 0; // init # of functions for this library
NewList (&libs[libnum].funcslist);
ptr = fc->filebuf; // starting from beginning of cached .fd file
while (*ptr) { // while not EOF
switch (scan_state) {
//-----------------------------------------------------------------
case NEED_NAME:
if (*ptr++ != '*') return FALSE;
if (*ptr++ != ' ') return FALSE;
if (*ptr++ != '"') return FALSE;
endquote = strchr(ptr, '"'); // find end of quoted str
if (!endquote) return FALSE;
*endquote++ = '\0'; // turn quoted string into C-string
libs[libnum].libname = ptr;
strlwr(ptr); // convert name to lower case
objtype = strchr(ptr,'.'); // find what type of "library"
if (FORCEOPEN) {
printf("Forced open option not implemented yet.\n");
// switch (*(objtype+1)) {
// case 'l': object = OpenLibrary(ptr); break;
// **!! case 'd': object = OpenDevice(ptr); break;
// case 'r': object = OpenResource(ptr); break;
// }
// } else {
} {
Forbid();
switch (*(objtype+1)) {
case 'l': // LIBRARY
object = FindName(&SysBase->LibList, ptr); break;
case 'd': // DEVICE
object = FindName(&SysBase->DeviceList, ptr); break;
case 'r': // RESOURCE
object = FindName(&SysBase->ResourceList, ptr); break;
}
// Make referenced object unswappable by bumping its Use counter !
if (object) ((struct Library*)object)->lib_OpenCnt++;
Permit();
}
if (!object) {
if (VERBOSE) printf("Object '%s' not available in memory.\n", ptr);
return FALSE;
}
else libs[libnum].openhandle = (struct Library *) object;
ptr = endquote; // goto next line;
scan_state = NEUTRAL;
break;
//-----------------------------------------------------------------
case NEUTRAL:
if (*(ptr-1) != LF) { // goto new line if not on bound
ptr = 1+ strchr(ptr, LF);
};
switch (*ptr) { // determine which line type
case '#': // this is and switch state
scan_state = GET_COMMAND; // accordingly
break;
case '*':
ptr++;
scan_state = NEUTRAL; break;
default:
scan_state = GET_FUNC; break;
}
break;
//-----------------------------------------------------------------
case GET_FUNC:
if (skip_private) { // don't collect system private
ptr++; // calls
bias += 6; // keep track of LVO though...
scan_state = NEUTRAL;
break;
}
endfunc = strchr(ptr, '('); // find '('
*endfunc = '\0'; // turn func name into C-string
funky = node_pool++; // allocate a new function node
funky->funcname = ptr; // fill in name related info
funky->namelen = endfunc - ptr;
funky->func_LVO = -bias; // fill in its _LVO value
funky->ctxt = NULL; // not patched yet...
funky->parent_lib= libs[libnum].openhandle; // link func to lib
AddTail(&libs[libnum].funcslist, (struct Node *)funky);
libs[libnum].numfuncs++; // track how many functions on list
total_funcs++; // and stored in total
if (VERBOSE) printf("FUNCTION: %s,\tLVO=-%d\n", ptr, bias);
ptr = 1 + endfunc; // point past \0 !
bias += 6; // keep track of LVO
scan_state = NEUTRAL;
break;
//-----------------------------------------------------------------
case GET_COMMAND:
ptr++;
if (*ptr++ != '#') return FALSE;
switch (*ptr++) {
case 'b':
if (*ptr == 'i') { // ##bias
ptr += 3;
bias = strtol(ptr, &newptr, 10); // get new bias
} else if (*ptr == 'a') { // ##base
// name of library base command is skipped
}
break;
case 'e': // ##end
ptr = ""; break; // end the scanning
case 'p':
if (*ptr == 'u') { // ##public
ptr += 5;
skip_private = FALSE;
} else if (*ptr == 'r') { // ##private
ptr += 6;
skip_private = TRUE;
}
break;
}
scan_state = NEUTRAL;
break;
//-----------------------------------------------------------------
} // switch (scan_state)
} // while (*ptr)
return TRUE; // Parsed entire .fd file without problems
}
//-------------------------------------------------------------------------
// Open main HeartBeat window (but finish off its menustrip first).
//-------------------------------------------------------------------------
BOOL popup_funcwindow (void) {
nw.LeftEdge = (screen->Width - nw.Width) /2; // center window on screen
if (!(window = OpenWindow(&nw))) // Open function selection window
return FALSE; // WITHOUT IDCMP flags
window->UserPort = IDCMPport; // use global Intuition event MsgPort
rp = window->RPort; // grab this window's RastPort ptr
append_lib_menus(); // Construct NewMenu array
menustrip = CreateMenus(mymenus, TAG_DONE); // Create MenuStrip
if (!menustrip) return FALSE; // if failed to alloc mem for it...
LayoutMenus(menustrip, vi, TAG_DONE); // Position Menus & Items
SetMenuStrip(window, menustrip); // Attach menu to window
OffMenu(window, FULLMENUNUM(OPTIONS_MENU,NOITEM,NOSUB)); // disable menu
menus_exist = TRUE;
ModifyIDCMP(window, IDCMP_SELECTION); // tell Intuition to start sending
return TRUE;
}
//-------------------------------------------------------------------------
// Part of the MenuStrip is dynamically generated at init time from the
// list of .fd files we found in the FD: directory.
// Here we finish off the rest of the array of NewMenu structs.
//
// Since we can have a very long list of libraries we have two menus which
// cope with the two alphabetical groups starting A-I and J-Z.
//-------------------------------------------------------------------------
void append_lib_menus(void){
int i;
struct NewMenu *newmenu;
lowlibs = 0; // count how many are in first menu
newmenu = mymenus + NEWMENU_APPEND; // point at the first empty slot
for (i=0; i<numlibs; i++) { // if lib name starts with A..I
if (toupper(libs[i].libname[0]) <= 'I') { // and .fd file was
if (libs[i].numfuncs) { // parsed succesfully
newmenu->nm_Type = NM_ITEM;
newmenu->nm_Label = libs[i].libname;
newmenu->nm_CommKey = NULL;
newmenu->nm_Flags = 0;
newmenu->nm_MutualExclude = 0L;
newmenu++;
lowlibs++;
}
}
}
// Now construct the Menu for Libraries J-Z.
newmenu->nm_Type = NM_TITLE; // Insert a MENU TITLE for 2nd Libraries
newmenu->nm_Label = "Libraries J-Z";
newmenu->nm_CommKey = NULL;
newmenu->nm_Flags = 0;
newmenu->nm_MutualExclude = 0L;
newmenu++; // and group all other libraries under 2nd
for (i=0; i<numlibs; i++) {
if (toupper(libs[i].libname[0]) >= 'J') {
if (libs[i].numfuncs) {
newmenu->nm_Type = NM_ITEM;
newmenu->nm_Label = libs[i].libname;
newmenu->nm_CommKey = NULL;
newmenu->nm_Flags = 0;
newmenu->nm_MutualExclude = 0L;
newmenu++;
}
}
}
// Add final end marker.
newmenu->nm_Type = NM_END; // Terminate NewMenu array with the required
newmenu->nm_Label = NULL; // end marker
newmenu->nm_CommKey = NULL;
newmenu->nm_Flags = 0;
newmenu->nm_MutualExclude = 0L;
}
//-------------------------------------------------------------------------
struct taskinfo { // struct is private to ask_task(), get_task_sel()
struct Node ti_node;
struct Task *ti_Task; // ptr to Task for when we get selection
char taskname[8+2+MAX_TASKLAB]; // 8 chars for hex Task ptr and N for name itself
};
//-------------------------------------------------------------------------
// User wants to change which Task he's snooping on.
//
// Traverse system Task lists and construct a local (static) list of Tasks
// with their names and hex TCB addresses for a LISTVIEW gadget to display.
// Open a window with this LISTVIEW and two more "USE", "CANCEL" gadgets.
// Don't wait for an answer here. Return immediately. Main loop will handle
// selection and the closing of this window.
//-------------------------------------------------------------------------
BOOL ask_task (char *wintitle) {
static struct TagItem ListViewTags[] = {
GTLV_Labels, NULL, // filled in with &taskList
GTLV_Top, 0,
LAYOUTA_SPACING, 1,
GTLV_ShowSelected, NULL, // add read-only label showing selected item
GTLV_Selected, 0,
GTLV_ScrollWidth, 16, // width of scroll bar
TAG_DONE
};
struct NewGadget ng = { // Not static coz we want stuff re-initialized
PANE_X+3,PANE_Y+2,
32*CHAR_WIDTH, 14*CHAR_HEIGHT,
"List of Tasks", NULL, NULL,
PLACETEXT_BELOW,
NULL, NULL
};
struct Gadget *lastgad;
struct NewWindow *nW;
NewList(&taskList); // initialize list header
Forbid(); // freeze view of system lists
// accumulate Tasks that are WAITing
collect_taskinfo(SysBase->TaskWait.lh_Head);
// Also give user access to Tasks which are currently ready to run.
// This is very useful since Tasks which crash usually use up their
// entire time quantum and therefore are constantly READY.
collect_taskinfo(SysBase->TaskReady.lh_Head);
Permit();
// Now that we've constructed a list of names suitable for a LISTVIEW,
// construct gadget and window.
ng.ng_VisualInfo = vi;
ng.ng_TextAttr = TAttr;
ng.ng_GadgetID = 0;
taskReqGads = NULL; lastgad = CreateContext( &taskReqGads );
ListViewTags[0].ti_Data = (ULONG) &taskList; // pass tasknames to Gadget
ng.ng_GadgetID = 0;
lastgad = CreateGadgetA(LISTVIEW_KIND, lastgad, &ng, (struct TagItem *) ListViewTags);
// Add "Use" and "Cancel" Gadgets
ng.ng_GadgetID++; ng.ng_GadgetText = "Use";
ng.ng_Flags = PLACETEXT_IN;
ng.ng_Width = 7*CHAR_WIDTH;
ng.ng_LeftEdge = 60;
ng.ng_TopEdge += ng.ng_Height + 8;
ng.ng_Height = CHAR_HEIGHT+3;
lastgad = CreateGadget(BUTTON_KIND, lastgad, &ng, TAG_DONE);
ng.ng_GadgetID++; ng.ng_GadgetText = "Cancel";
ng.ng_LeftEdge = 150;
CreateGadget(BUTTON_KIND, lastgad, &ng, TAG_DONE);
nW = &nw;
nW->Title = wintitle;
nW->TopEdge = 40;
nW->LeftEdge = 100;
nW->Width = 274;
nW->Height = 150;
nW->Flags = WINDOWCLOSE| ACTIVATE| WINDOWDEPTH| WINDOWDRAG;
nW->IDCMPFlags = 0;
taskwindow = OpenWindow(nW); // open task requester window
AddGList(taskwindow, taskReqGads, -1, -1, NULL); // append at end, all gadgets
RefreshGList(taskReqGads, taskwindow, NULL, -1); // display new ones
GT_RefreshWindow(taskwindow, NULL);
taskwindow->UserPort = IDCMPport; // use global event port
ModifyIDCMP(taskwindow, IDCMP_SELECTION); // enable transmissions
app_state = TASKWINDOW; task_num = 0; // first item is selected by default
return TRUE;
}
//-------------------------------------------------------------------------
// Traverse Task/Process list and collect name, TCB addr and CLI num info
//-------------------------------------------------------------------------
void collect_taskinfo (struct Node *task) {
struct taskinfo *node;
int len,cli;
while (task->ln_Succ) {
node = (struct taskinfo*) AllocMem(sizeof (struct taskinfo),MEMF_PUBLIC);
node->ti_Task = (struct Task*) task;
node->ti_node.ln_Name = node->taskname;
// make sure both fields aren't separated by a \0
strcpy(node->taskname, " "); // pad with spaces
len = stcl_h(node->taskname, (int) task); // print task address
stccpy(node->taskname+10, task->ln_Name, MAX_TASKLAB); // and taskname
*(node->taskname + len) = ' '; // glue both strings together
if (task->ln_Type == NT_PROCESS) { // If a PROCESS also print CLI num
cli = ((struct Process*)task)->pr_TaskNum;
if (cli) { // only non-zero CLI nums valid
*(node->taskname +8 ) = '0' +cli;
}
}
AddTail(&taskList, (struct Node *) node);
task = task->ln_Succ;
}
}
//-------------------------------------------------------------------------
// User clicked on CLOSEWINDOW, USE or CANCEL gadget and we have to release
// our Task information list, gadget memory and window.
//-------------------------------------------------------------------------
void kill_taskReq_window(void) {
struct taskinfo *node,*nodex;
node = (struct taskinfo*) taskList.lh_Head;
while (nodex = (struct taskinfo*) node->ti_node.ln_Succ) {
FreeMem((APTR) node, sizeof (struct taskinfo));
node = nodex;
}
CloseWindowSafely(taskwindow); taskwindow = NULL;
FreeGadgets(taskReqGads); // free CreateGadget RAM
}
//-------------------------------------------------------------------------
// Find Task # N in our Task information list and point SNOOP_TASK to it.
//-------------------------------------------------------------------------
void watch_task(int index) {
struct taskinfo *node;
node = (struct taskinfo*) taskList.lh_Head;
while(index--) {
// just follow list and find node number N
node = (struct taskinfo*) node->ti_node.ln_Succ;
}
SNOOP_TASK = node->ti_Task; // set address of Task to be snooped
}
//-------------------------------------------------------------------------
// Pop up the register match window sprinkled with loads of GadTools Gadgets.
// HeartBeat allows us to monitor functions which get specific arguments.
// This is done by specifying the contents of the data and address registers
// when the system function gets called.
// Masks are used to ignore whole registers or parts of registers.
// When a function is being traced in this "arguments" mode and the arguments
// match the template then HeartBeat will either simply increment a counter
// or FREEZE the calling Task.
//-------------------------------------------------------------------------
BOOL ask_register_info (char *wintitle, struct Context *ctxt) {
int regs;
char *hexstr="$xxxxyyyy";
static char *actions[] = { "Count on match",
"Freeze on match", NULL };
static char *regnames[] = { "D0","D1","D2","D3","D4","D5","D6","D7",
"A0","A1","A2","A3","A4","A5","A6","A7" };
struct NewGadget ng = {
PANE_X, PANE_Y+12,
12*CHAR_WIDTH, CHAR_HEIGHT+4,
NULL, NULL, NULL,
0,
NULL, NULL };
struct Gadget *lastgad;
struct NewWindow *nW;
ctxt->freeze = ctxt->frozen = FALSE; // sync up with what Gadget shows!
ng.ng_VisualInfo = vi;
ng.ng_TextAttr = TAttr;
ng.ng_GadgetID = 0;
regsReqGads = NULL; lastgad = CreateContext( ®sReqGads );
// Create 16 String Gadgets. Two per 680x0 register for value and mask.
for (regs=0; regs < 16; regs++) {
sprintf(hexstr, "$%lX", ctxt->regvals[regs]);
ng.ng_Flags = PLACETEXT_RIGHT;
ng.ng_GadgetText = regnames[regs];
lastgad = CreateGadget(STRING_KIND, lastgad, &ng, GTST_String, hexstr, TAG_DONE);
sprintf(hexstr, "$%lX", ctxt->regmasks[regs]);
ng.ng_GadgetID++;
ng.ng_GadgetText = NULL;
ng.ng_Flags = 0;
ng.ng_LeftEdge += 10*CHAR_WIDTH + 48;
lastgad = CreateGadget(STRING_KIND, lastgad, &ng, GTST_String, hexstr, TAG_DONE);
ng.ng_LeftEdge -= 10*CHAR_WIDTH + 48;
ng.ng_TopEdge += CHAR_HEIGHT+4 +1;
ng.ng_GadgetID++;
}
// Add Trapping mode (Cycleing) Gadget
ng.ng_Flags = PLACETEXT_IN | NG_HIGHLABEL;
ng.ng_Width = 20*CHAR_WIDTH;
ng.ng_LeftEdge = PANE_X;
ng.ng_TopEdge += 10;
lastgad = CreateGadget(CYCLE_KIND, lastgad, &ng, GTCY_Labels, actions, TAG_DONE);
// Add "Use" and "Cancel" Gadgets
ng.ng_Flags = PLACETEXT_IN;
ng.ng_GadgetID++; ng.ng_GadgetText = "Use";
ng.ng_Width = 7*CHAR_WIDTH;
ng.ng_LeftEdge = 44;
ng.ng_TopEdge += 20;
lastgad = CreateGadget(BUTTON_KIND, lastgad, &ng, TAG_DONE);
ng.ng_GadgetID++; ng.ng_GadgetText = "Cancel";
ng.ng_LeftEdge += 90;
lastgad = CreateGadget(BUTTON_KIND, lastgad, &ng, TAG_DONE);
// Open Window for Gadgets to live in
nW = &nw;
nW->Title = wintitle;
nW->TopEdge = 40; nW->LeftEdge = 100;
nW->Width = 236; nW->Height = 280;
nW->Flags = WINDOWCLOSE| ACTIVATE| WINDOWDEPTH| WINDOWDRAG;
nW->IDCMPFlags = 0;
regswindow = OpenWindow(nW); //**!! error
regsrp = regswindow->RPort;
AddGList(regswindow, regsReqGads, -1, -1, NULL);
RefreshGList(regsReqGads, regswindow, NULL, -1);
GT_RefreshWindow(regswindow, NULL);
Move(regsrp, PANE_X+28, PANE_Y+7);
SetAPen(regsrp,1);
Text(regsrp,"Values Masks",20);
regswindow->UserPort = IDCMPport; // use global event port
ModifyIDCMP(regswindow, IDCMP_SELECTION); // enable transmissions
app_state = REGSWINDOW;
// Add this stage the window sits there with all its Gadgets waiting for
// input; but this function returns now, so it's the main event loop that
// picks up the user selections and then eventually kills the window
// when "Use", "Cancel" or the CLOSEWINDOW Gadget gets selected.
return TRUE;
}
//-------------------------------------------------------------------------
// User clicked on CLOSEWINDOW, USE or CANCEL gadget and we have to release
// our Window and gadget memory.
//-------------------------------------------------------------------------
void kill_regsReq_window(void) {
CloseWindowSafely(regswindow); regswindow = NULL;
FreeGadgets(regsReqGads); // free CreateGadget RAM
}
//-------------------------------------------------------------------------
// Turn a cached text file into a collection of C-strings (a heap of strings)
//-------------------------------------------------------------------------
void text_to_heap( struct FileCache *fc) {
register int i;
register char * ptr;
ptr = fc->filebuf;
for (i=fc->bufsize; i; i--) {
if (*ptr == LF) // replace all LF chars by string terminators
*ptr = '\0';
ptr++;
}
}
//-------------------------------------------------------------------------
// Turn a LF-terminated string into a proper C-string.
//-------------------------------------------------------------------------
void strip_lf (char *str) {
while (*str++ != LF) // go find terminating LF
;
*(str-1) = 0; // and replace by a \0
}
//-------------------------------------------------------------------------
// Close Window safely (taking care of possible queued messages)
//-------------------------------------------------------------------------
void CloseWindowSafely( struct Window *win ) {
if (win->UserPort) {
Forbid(); // we forbid here to keep out of race conditions with Intuition
// return any messages for this window that have not yet been processed
StripIntuiMessages( win->UserPort, win );
win->UserPort = NULL; // clear UserPort so Intuition will not free it
ModifyIDCMP( win, 0L ); // tell inuition to stop sending more messages
Permit(); // turn tasking back on
}
CloseWindow( win ); // and really close the window
}
//-------------------------------------------------------------------------
// remove and reply to all IntuiMessages on a port that have been sent to a
// particular window (note that we don't rely on the ln_Succ pointer of a
// message after we have replied it)
//-------------------------------------------------------------------------
void StripIntuiMessages(struct MsgPort *mp, struct Window *win ) {
register struct IntuiMessage *msg;
register struct Node *succ;
msg = (struct IntuiMessage *) mp->mp_MsgList.lh_Head; // 1st message
while( succ = msg->ExecMessage.mn_Node.ln_Succ ) {
if( msg->IDCMPWindow == win ) { // message addressed
// to us ?
// Intuition is about to free this message.
// Make sure that we have politely sent it back.
Remove((struct Node*)msg);
ReplyMsg((struct Message*)msg);
}
msg = (struct IntuiMessage *) succ; // next IntuiMessage on list
}
}
//-------------------------------------------------------------------------
// Open all libraries, initialize arrays, get private memory pool,
// open two IPC Message Ports (one for timeout messages, one for all Intuition
// messages), open timer.device, set global function tracking mode.
//-------------------------------------------------------------------------
BOOL init_program(void) {
int i;
if (!(AslBase = (struct AslBase *) OpenLibrary( "asl.library",0L)))
return FALSE;
if (!(GadToolsBase = (struct GadToolsBase *) OpenLibrary( "gadtools.library",0L)))
return FALSE;
if (!(IntuitionBase = (struct IntuitionBase*) OpenLibrary("intuition.library",0L)))
return FALSE;
if (!(GfxBase = (struct GfxBase *) OpenLibrary( "graphics.library",0L)))
return FALSE;
screen = IntuitionBase->ActiveScreen; // note host Screen
TAttr = screen->Font; // note Screen default Font
vi = GetVisualInfo(screen, TAG_DONE);
// Mark all file caches as empty
FdDirCache.bufsize = 0;
for (i=0; i< MAX_LIBS; i++) {
fd_caches[i].bufsize = 0; // all caches are empty
libs[i].openhandle = NULL; // all libs are unopened
}
// Mark all patched system call links as invalid
monitored = 0; // no calls monitored yet..
for (i=0; i< MAX_PATCHED; i++) { // no patched calls yet...
patched_calls[i] = NULL;
}
// Get a large memory pool to hold System Call information
pool_base = node_pool = (struct SysCall *) AllocMem(POOL_SIZE, MEMF_PUBLIC|MEMF_CLEAR);
if (!pool_base) {
printf("Unable to allocate memory pool.\n");
return FALSE;
}
// Create our event message ports
if (!(timerport = CreateMsgPort())) return FALSE;
if (!(IDCMPport = CreateMsgPort())) return FALSE;
if (OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest*) &timereq, 0)) {
printf("Failed to open timer.device\n");
return FALSE;
}
timereq.tr_node.io_Message.mn_ReplyPort = timerport;
SNOOP_TASK = NULL; // global tracking mode on
monwindow = taskwindow = regswindow = NULL; // no special windows up
return TRUE;
}
//-------------------------------------------------------------------------
// Unpatch any patched functions.
// Unlock resources (Libraries, Devices, Resources)
// Deallocate all file buffers still in use.
//-------------------------------------------------------------------------
void cleanup(void) {
int i,size;
unpatch_functions();
// Release all the .FD file caches
for (i=0; i< MAX_LIBS; i++) {
size = fd_caches[i].bufsize;
if (size) {
FreeMem(fd_caches[i].filebuf, size);
}
}
size = FdDirCache.bufsize;
if (size)
FreeMem(FdDirCache.filebuf, size);
// Release all the libraries/devices/resources which we locked
for (libnum=0; libnum<numlibs; libnum++) {
if (libs[libnum].openhandle)
libs[libnum].openhandle->lib_OpenCnt--;
}
// Free all SysCall structures (pool)
if (pool_base)
FreeMem((char*)pool_base, POOL_SIZE);
if (IntuitionBase) {
if (window) {
window->UserPort = NULL; // Intuition: leave MY port alone!
if (menus_exist) {
ClearMenuStrip(window);
FreeMenus(menustrip);
}
CloseWindowSafely(window);
}
FreeVisualInfo(vi);
CloseLibrary( (struct Library *) IntuitionBase);
}
if (AslBase) {
CloseLibrary( (struct Library *) AslBase);
}
if (GadToolsBase) {
CloseLibrary( (struct Library *) GadToolsBase);
}
if (GfxBase) {
CloseLibrary( (struct Library *) GfxBase);
}
if (timerport) DeleteMsgPort((struct MsgPort*)timerport);
if (IDCMPport) DeleteMsgPort((struct MsgPort*)IDCMPport);
CloseDevice((struct IORequest*) &timereq);
exit(0);
}
//-------------------------------------------------------------------------
// For all functions which we've patched to track their use, undo all.
//-------------------------------------------------------------------------
void unpatch_functions(void) {
int i;
struct SysCall *syscall;
if (!monitored) return; // if nothing to unpatch... don't
// Unpatch all functions which HeartBeat patched.
for (i=0; i<monitored; i++) {
syscall = patched_calls[i];
if (VERBOSE) printf("Unpatching Call %s, %x \n",
syscall->funcname, syscall->ctxt->stdvector);
SetFunction(syscall->parent_lib, syscall->func_LVO, syscall->ctxt->stdvector );
syscall->ctxt = NULL;
}
ctxt_pool = (void*) node_pool; // reset Contexts pool ptr
monitored = 0; // reset # of monitored functions
}
//-------------------------------------------------------------------------